%%%-------------------------------------------------------------------
%% @doc mqeg_publisher public API
%% @end
%%%-------------------------------------------------------------------

-module(mqeg_publisher).
-behaviour(gen_server).

-include("debug.hrl").
-include_lib("amqp_client/include/amqp_client.hrl").

%% --------------------------------------------------------------------
%% External exports
%% --------------------------------------------------------------------
-export([start_link/1]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).

-record(state, {connection, channel, queue, routing_key, exchange_declare, queue_declare, queue_binding, basic_publish}).
%% ====================================================================
%% External functions
%% ====================================================================
start_link(Params) ->
    gen_server:start_link(?MODULE, Params, []).


%% ====================================================================
init(Name) ->
    ?trace("mqeg_publisher Name: ~p", [Name]),
    ExchangeDeclare = exchange_declare(),
    QueueDeclare    = queue_declare(Name),
    QueueBinding    = queue_binding(Name),
    BasicPublish    = basic_publish(Name),

    Connection      = mqeg_misc:get_singleton(connection),
    {ok, Channel}   = amqp_connection:open_channel(Connection),

    #'exchange.declare_ok'{} = amqp_channel:call(Channel, ExchangeDeclare),
    #'queue.declare_ok'{}    = amqp_channel:call(Channel, QueueDeclare),
    #'queue.bind_ok'{}       = amqp_channel:call(Channel, QueueBinding),

    {ok, #state{ connection       = Connection
               , channel          = Channel
               , queue            = Name
               , routing_key      = Name
               , exchange_declare = ExchangeDeclare
               , queue_declare    = QueueDeclare
               , queue_binding    = QueueBinding
               , basic_publish    = BasicPublish
               }
    }.


handle_call(_Info, _From, State) ->
    ?trace(_Info),
    {reply, ok, State}.


handle_cast(Info, State) ->
    try 
        do_cast(Info, State)
    catch Class:Reason:Stack ->
        ?trace("mqeg_publisher error, Class:~p, Reason:~p, Stack:~p", [Class, Reason, Stack]),
        {noreply, State}
    end.


handle_info(_Info, State) ->
    ?trace(_Info),
    {noreply, State}.


terminate(_Reason, State) ->
    amqp_channel:close(State#state.channel),
    mqeg_publishers_sup:del_publisher(State#state.queue),
    ok.


code_change(_oldvsn, Status, _extra) ->
    {ok, Status}.


%% ====================================================================
%% Local functions
%% ====================================================================
do_cast(stop, State) ->
    {stop, normal, State};

do_cast(show, State) ->
    ?trace("connection: ~p; "
           "channel: ~p; "
           "queue: ~p; "
           "routing_key: ~p; "
           "exchange_declare: ~p; "
           "queue_declare: ~p; "
           "queue_binding: ~p; "
           "basic_publish: ~p; "
          , tl( tuple_to_list(State) )),
    {noreply, State};

do_cast({msg, Bin}, State) ->
    ?trace(#amqp_msg{payload = Bin}),
    amqp_channel:cast(State#state.channel, State#state.basic_publish, #amqp_msg{payload = Bin}),
    {noreply, State};

do_cast(_Info, State) ->
    ?trace(_Info),
    {noreply, State}.



%% ====================================================================
exchange_declare() ->
    {ok, Config} = application:get_env(mqeg, exchange),
    #'exchange.declare'{ exchange = proplists:get_value(exchange, Config)
                       , type     = proplists:get_value(type,     Config)
                       , durable  = proplists:get_value(durable,  Config)
                       }.

queue_declare(Name) ->
    {ok, Config} = application:get_env(mqeg, queue),
    #'queue.declare'{ queue       = Name
                    , durable     = proplists:get_value(durable,  Config)
                    , exclusive   = proplists:get_value(exclusive,Config)
                    }.

queue_binding(Name) ->
    {ok, Config} = application:get_env(mqeg, exchange),
    #'queue.bind'{ queue          = Name
                 , exchange       = proplists:get_value(exchange, Config)
                 , routing_key    = Name
                 }.

basic_publish(Name) ->
    {ok, Config} = application:get_env(mqeg, exchange),
    #'basic.publish'{ exchange    = proplists:get_value(exchange, Config)
                    , routing_key = Name
                    , mandatory   = false
                    , immediate   = false
                    }.
